package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.cart.feign.CartFeignClient;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.util.GmallThreadLocalUtils;
import com.atguigu.gmall.feign.ProductFeignClient;
import com.atguigu.gmall.model.base.BaseEntity;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.model.payment.PaymentInfo;
import com.atguigu.gmall.order.mapper.OrderDetailMapper;
import com.atguigu.gmall.order.mapper.OrderMapper;
import com.atguigu.gmall.order.mapper.PaymentInfoMapper;
import com.atguigu.gmall.order.service.OrderService;
import com.atguigu.gmall.payment.feign.WxPayFeignClient;
import com.atguigu.gmall.payment.feign.ZfbPayFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/***
 * 普通订单相关的service层接口的实现类
 */
@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private CartFeignClient cartFeignClient;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private WxPayFeignClient wxPayFeignClient;
    @Resource
    private PaymentInfoMapper paymentInfoMapper;
    @Autowired
    private ZfbPayFeignClient zfbPayFeignClient;

    /**
     * 新增订单
     *
     * @param orderInfo
     */
    @Override
    public void save(OrderInfo orderInfo) {
        //获取分布式锁
        RLock lock = redissonClient.getLock("Add_Order_Lock_" + GmallThreadLocalUtils.get());

        try {
            if (lock.tryLock()) {
                //加锁成功
                try {
                    //为防止死锁为锁设置过期时间
                    redisTemplate.expire("Add_Order_Lock_" + GmallThreadLocalUtils.get(), 10, TimeUnit.SECONDS);

                    //查询到用户本次购买的商品列表等信息
                    Map orderCartMap = cartFeignClient.getOrderCartMap();
                    if (orderCartMap == null) {
                        return;
                    }

                    //封装订单表数据
                    orderInfo.setTotalAmount(new BigDecimal(orderCartMap.get("amount").toString()));
                    orderInfo.setOrderStatus(OrderStatus.UNPAID.getComment());
                    orderInfo.setUserId(GmallThreadLocalUtils.get());
                    orderInfo.setPaymentWay("1");
                    orderInfo.setCreateTime(new Date());
                    orderInfo.setExpireTime(new Date(System.currentTimeMillis() + 1800000));
                    orderInfo.setProcessStatus(ProcessStatus.UNPAID.getComment());

                    //向订单表插入数据
                    int insert = orderMapper.insert(orderInfo);
                    if (insert <= 0) {
                        throw new GmallException("新增订单失败", null);
                    }

                    //获取订单id
                    Long orderInfoId = orderInfo.getId();
                    //获取购物车列表
                    List cartInfoList = (List) orderCartMap.get("cartInfoList");
                    //封装订单表详情数据，获取减库存的map
                    Map decountStockMap = this.saveOrderDetail(orderInfoId, cartInfoList);

                    //清空购物车
//                    if(!cartFeignClient.clearCart()) {
//                        //清空失败
//                        throw new GmallException("清空购物车失败,下单失败!",null);
//                    }

                    //扣减库存
                    Boolean decount = productFeignClient.decount(decountStockMap);
                    if (!decount) {
                        throw new GmallException("扣减库存失败,下单失败!", null);
                    }

                    //发送延迟消息，防止用户下单不付款---TODO
                    rabbitTemplate.convertAndSend(
                            "order_normal_exchange",
                            "order.normal",
                            orderInfoId + "",
                            message -> {
                                //获取消息对象的属性
                                MessageProperties messageProperties = message.getMessageProperties();
                                //设置过期时间
                                messageProperties.setExpiration(600000 + "");
                                //返回
                                return message;
                            }
                    );

                } catch (Exception e) {
                    log.error("新增订单加锁成功，但新增订单业务逻辑出现异常，原因为：" + e.getMessage());
                    throw e;
                } finally {
                    //释放锁
                    lock.unlock();
                }
            } else {
                //加锁失败，有重复下单、多端下单、回退下单等情况
                throw new GmallException("不允许重复下单、多端下单、回退下单！！！", null);
            }
        } catch (Exception e) {
            log.error("下单加锁出现异常，原因为：" + e.getMessage());
            throw e;
        }
    }

    /**
     * 用户主动/超时取消订单
     *
     * @param orderId
     */
    @Override
    public void cancel(Long orderId) {
        //获取分布式锁
        RLock lock = redissonClient.getLock("order_cancel_update_lock_" + orderId);

        try {
            //加锁
            if (lock.tryLock()) {
                try {
                    //防止死锁，给锁设置一个过期时长
                    redisTemplate.expire("order_cancel_update_lock_" + orderId, 10, TimeUnit.SECONDS);

                    //初始化拼接查询条件
                    LambdaQueryWrapper<OrderInfo> wrapper =
                            new LambdaQueryWrapper<OrderInfo>()
                                    .eq(BaseEntity::getId, orderId)
                                    .eq(OrderInfo::getOrderStatus, OrderStatus.UNPAID.getComment());
                    //获取用户名
                    String username = GmallThreadLocalUtils.get();
                    //初始化订单状态信息
                    String msg = "";
                    //判断用户是主动取消还是超时自动取消
                    if (StringUtils.isEmpty(username)) {
                        //为空，说明是超时自动取消
                        //超时订单状态信息赋值
                        msg = OrderStatus.TIMEOUT_CANCEL.getComment();
                    } else {
                        //不为空，说明是主动取消
                        //追加查询条件
                        wrapper.eq(OrderInfo::getUserId, username);

                        //手动取消订单状态信息赋值
                        msg = OrderStatus.CANCEL.getComment();
                    }

                    //1 根据订单id和用户名字查询到订单
                    OrderInfo orderInfo = orderMapper.selectOne(wrapper);
                    if (orderInfo == null ||
                            orderInfo.getId() == null) {
                        return;
                    }

                    //2 修改订单的状态和进度状态
                    orderInfo.setOrderStatus(msg);
                    orderInfo.setProcessStatus(msg);
                    //2.1 修改数据库中的数据
                    int i = orderMapper.updateById(orderInfo);
                    if (i <= 0) {
                        throw new GmallException("取消订单失败！", null);
                    }

                    //3 用订单id查询到订单详情集合，且回滚库存
                    this.rollbackStock(orderId);
                } catch (Exception e2) {
                    log.error("加锁成功，但取消订单逻辑有异常，原因为：" + e2.getMessage());
                } finally {
                    //释放锁
                    lock.unlock();
                }
            }
        } catch (Exception e) {
            log.error("加锁失败，原因为：" + e.getMessage());
        }
    }

    /**
     * 修改，支付成功后的，订单状态，无条件修改为：已支付！！！
     *
     * @param messageBodyJsonString
     */
    @Override
    public void update(String messageBodyJsonString) {
        //反序列化
        Map<String, String> messageBodyMap = JSONObject.parseObject(messageBodyJsonString, Map.class);
        //获取用户的支付方式
        String paymentType = messageBodyMap.get("payment_type");
        //获取订单id
        String orderId = messageBodyMap.get("out_trade_no");

        //有并发、重复下单的问题，需要加锁
        //获取锁
        RLock lock = redissonClient.getLock("order_cancel_update_lock_" + orderId);
        try {
            //加锁，卡住，直到加锁成功!!!
            lock.lock();
            //防止死锁，设置过期时间
            redisTemplate.expire("order_cancel_update_lock_" + orderId, 10, TimeUnit.SECONDS);
            try {
                //2 查询出订单信息
                OrderInfo orderInfo = orderMapper.selectById(orderId);
                if (orderInfo == null || orderInfo.getId() == null) {
                    //为空直接结束方法
                    return;
                }

                //判断订单状态
                if (orderInfo.getOrderStatus().equals(OrderStatus.UNPAID.getComment())) {
                    //状态为：未支付 --> 直接修改
                    //3 修改订单
                    //填充数据
                    orderInfo.setOrderStatus(OrderStatus.PAID.getComment());
                    orderInfo.setProcessStatus(OrderStatus.PAID.getComment());
                    //根据支付平台不同，设置第三方交易订单号
                    String outTradeNo = paymentType.equals("WX") ? messageBodyMap.get("transaction_id") : messageBodyMap.get("trade_no");
                    orderInfo.setOutTradeNo(outTradeNo);
                    orderInfo.setTradeBody(messageBodyJsonString);
                    //修改数据库数据
                    int update = orderMapper.updateById(orderInfo);
                    //判断
                    if (update <= 0) {
                        throw new GmallException("支付完成，修改订单状态失败！", null);
                    }
                } else if (orderInfo.getOrderStatus().equals(OrderStatus.CANCEL.getComment()) ||
                        orderInfo.getOrderStatus().equals(OrderStatus.TIMEOUT_CANCEL.getComment())) {
                    //状态为：主动取消或超时取消，则执行退款业务逻辑--mq
                    //发送消息
                    sendRefundMessage(orderId, orderInfo, paymentType);
                } else if (orderInfo.getOrderStatus().equals(OrderStatus.PAID.getComment())) {
                    //订单状态为：已支付 --> 判断用户是否重复支付
                    //查询用户上一次支付类型
                    PaymentInfo paymentInfo =
                            paymentInfoMapper.selectOne(
                                    new LambdaQueryWrapper<PaymentInfo>()
                                            .eq(PaymentInfo::getOrderId, orderId)
                                            .eq(PaymentInfo::getIsDelete, 1));

                    //判断是否在不同的支付平台付过钱
                    if (paymentInfo.getPaymentType().equals(paymentType)) {
                        //不相等，退款---mq
                        //发送消息
                        sendRefundMessage(orderId, orderInfo, paymentType);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error("加锁成功，但执行修改逻辑有异常，原因为：" + e.getMessage());
            } finally {
                //释放锁
                lock.unlock();
            }

        } catch (Exception e) {
            log.error("加锁出现异常，原因为：" + e.getMessage());
            throw e;
        }
    }

    /**
     * 使用mq发送退款消息
     *
     * @param orderId
     * @param orderInfo
     */
    private void sendRefundMessage(String orderId, OrderInfo orderInfo, String paymentType) {
        //封装微信退款所需参数
        Map<String, String> refundParamMap = new HashMap<>();
        refundParamMap.put("orderId", orderId);//商户订单号
        refundParamMap.put("refundNo", UUID.randomUUID().toString().replace("-", ""));//商户退款单号
        refundParamMap.put("amount", orderInfo.getTotalAmount().multiply(new BigDecimal("100")).intValue() + "");//订单金额
        refundParamMap.put("payment_type", paymentType);//支付类型
        //发送消息
        rabbitTemplate.convertAndSend("order_refund_exchange",
                "order.refund",
                JSONObject.toJSONString(refundParamMap));
    }

    /**
     * 根据用户选择不同的支付渠道，获取指定订单的支付二维码信息
     *
     * @param orderId
     * @param paymentType
     * @return
     */
    @Override
    public String getPaymentInfo(String orderId, String paymentType) {
        //初始化支付地址
        String payUrl = "";
        //如用户已经申请过支付二维码，则返回旧的
        payUrl = (String) redisTemplate.opsForValue().get("order_pay_url" + orderId);
        if (!StringUtils.isEmpty(payUrl)) {
            //不为空，用户在5分钟内有申请，直接返回
            return payUrl;
        }

        //防止用户频繁申请支付二维码，加锁，让其每5分钟申请一次
        Boolean flag =
                redisTemplate
                        .opsForValue()
                        .setIfAbsent("order_pay_url" + orderId, "123", 300, TimeUnit.SECONDS);
        //加锁
        if (flag) {
            //获取订单信息数据
            OrderInfo orderInfo =
                    orderMapper.selectOne(
                            new LambdaQueryWrapper<OrderInfo>().
                                    eq(BaseEntity::getId, orderId).
                                    eq(OrderInfo::getUserId, GmallThreadLocalUtils.get()));
            //1 获取二维码---feign--测试代码-TODO
            //根据不同的支付平台做判断
            if (paymentType.equals("WX")) {
                //微信支付
                //远程调用，获取支付地址
                payUrl = wxPayFeignClient.getPayUrl("尚硅谷商城==微信支付",
                        "java082866666" + orderId,
                        orderInfo.getTotalAmount().multiply(new BigDecimal("100")).intValue() + "");
            } else {
                //支付宝支付
                //远程调用，获取支付地址
                payUrl = zfbPayFeignClient.getPayPage("尚硅谷商城==支付宝支付",
                        "java082866666" + orderId,
                        orderInfo.getTotalAmount().doubleValue() + "");
            }
            if (payUrl == null) {
                //支付地址为空
                return null;
            }

            //考虑到用户同笔订单再次申请支付地址，先逻辑删除上一笔
            int delete = paymentInfoMapper.deletePaymentInfo(orderId);
            if (delete < 0) {
                //删除失败，再把key删除,防止用户不能申请渠道
                redisTemplate.delete("order_pay_url_" + orderId);
                return null;
            }
            //2 保存paymentInfo
            PaymentInfo paymentInfo = new PaymentInfo();
            //封装数据
            paymentInfo.setPayUrl(payUrl);//支付地址
            paymentInfo.setOrderId(Long.parseLong(orderId));
            paymentInfo.setPaymentType(paymentType);
            paymentInfo.setTotalAmount(orderInfo.getTotalAmount());
            paymentInfo.setSubject("尚硅谷商城");
            paymentInfo.setPaymentStatus(OrderStatus.UNPAID.getComment());
            paymentInfo.setCreateTime(new Date());
            //保存到数据库
            int insert = paymentInfoMapper.insert(paymentInfo);
            if (insert > 0) {
                //保存成功，将数据同步到redis中
                redisTemplate.opsForValue().set("order_pay_url" + orderId, payUrl);
            }

            //3 返回结果
            return payUrl;
        }
        return null;
    }

    /**
     * 用订单id查询到订单详情集合，且回滚库存
     *
     * @param orderId
     */
    private void rollbackStock(Long orderId) {
        //1 查询到该订单的详情
        List<OrderDetail> orderDetailList =
                orderDetailMapper.selectList(
                        new LambdaQueryWrapper<OrderDetail>()
                                .eq(OrderDetail::getOrderId, orderId));

        //声明map
        Map rollbackStockMap = new ConcurrentHashMap();
        //遍历
        orderDetailList.stream().forEach(orderDetail -> {
            //2 把skuId --> skuNum封装为map
            rollbackStockMap.put(orderDetail.getSkuId().toString(), orderDetail.getSkuNum());
        });

        //3 远程调用product微服务回滚库存
        if (!productFeignClient.rollbackStock(rollbackStockMap)) {
            throw new GmallException("回滚库存失败!", null);
        }
    }

    /**
     * 保存订单详情表数据
     *
     * @param orderInfoId
     * @param cartInfoList
     * @return
     */
    private Map saveOrderDetail(Long orderInfoId, List cartInfoList) {
        //初始化减库存的map
        Map decountStockMap = new ConcurrentHashMap();

        cartInfoList.stream().forEach(o -> {
            //序列化
            String s = JSONObject.toJSONString(o);
            //反序列化
            CartInfo cartInfo = JSONObject.parseObject(s, CartInfo.class);
            //初始化订单详情对象
            OrderDetail orderDetail = new OrderDetail();

            //封装数据
            orderDetail.setOrderId(orderInfoId);
            orderDetail.setSkuId(cartInfo.getSkuId());
            orderDetail.setSkuName(cartInfo.getSkuName());
            orderDetail.setImgUrl(cartInfo.getImgUrl());
            orderDetail.setOrderPrice(cartInfo.getSkuPrice());
            orderDetail.setSkuNum(cartInfo.getSkuNum());

            //插入到数据库
            int insert = orderDetailMapper.insert(orderDetail);
            if (insert <= 0) {
                throw new GmallException("新增订单详情失败", null);
            }

            //封装减库存的map 每个skuId --> 数量
            decountStockMap.put(cartInfo.getSkuId().toString(), cartInfo.getSkuNum().toString());
        });

        //返回
        return decountStockMap;
    }
}